#include<cstdio>
#include"logger.h"

#include"config.h"

using namespace std;


/**
 * This function is passed to fuse_opt_parse. It parses any arguments that are specific to PostgreFS and adds the rest
 * to outargs
 * 
 *  \return on error: -1 \n on sucess: 0
 */
int Config::arg_processor(void *data, const char *arg, int key, struct fuse_args *outargs)
{   if(key != FUSE_OPT_KEY_OPT)
    {   fuse_opt_add_arg(outargs, arg);
        return 0;
    }
    int result = ((Config*)data)->process_option(arg);
    if (result<0) //option not found
    {   fuse_opt_add_arg(outargs, arg);
        return 0;
    }
    return result;
}


void Config::init()
{   host = NULL;
    port = NULL;
    user = NULL;
    password = NULL;
    database = NULL;
    init_conn_pool = 4;
    max_conn_pool = 32;
    static struct fuse_args initial_args = FUSE_ARGS_INIT(0, NULL);
    args = initial_args;
}

Config::Config()
{   init();
}

Config::Config(int argc, char ** argv)
{   init();
    set_args(argc, argv);
}


/**
 * Takes the options from a file and for each line calls Config::process_option. The file is limited to 64 lines
 * and should look like :
 * \n user=postgrefs \n host=localhost \n database=postgrefs \n logfile=logfile \n password=password \n\n
 * 
 * For supported options see Config::arg_processor
 * 
 * 
 * \return on error: -1 \n on sucess: 0
 */
int Config::load_config_from_file(const char * filename)
{   FILE * file = fopen(filename, "r");
    if (file==NULL)
    {   perror(filename);
        exit(1);
    }

    char * option;
    size_t nbytes;
    const int MAXLINES=64;
    for (int i=0;i<MAXLINES;++i)
    {   nbytes = 32;
        option = (char *) malloc(nbytes+1);
        if (getline(&option, &nbytes, file)==-1) //EOF
            return 0;
        if (process_option(option) < 0)
            fprintf(stderr, "WARNING: unrecognized option %s.\n", option);
    }

    // MAX LINES READ
    fprintf(stderr, "WARNING: max lines limit on configuration file reached.\n");
    return 0;
}


/**
 * Takes a single option and updates the configuration. For supported options see Config::arg_processor
 * 
 * @param arg a string to be parsed
 * 
 * \return on error: -1 \n on sucess: 0
 */
int Config::process_option(const char * arg)
{   
    if(!strncmp(arg, "host=", strlen("host=")))
    {   host = strchr(arg, '=') + 1;
        return 0;
    }

    if(!strncmp(arg, "port=", strlen("port=")))
    {   port = strchr(arg, '=') + 1;
        return 0;
    }
    
    if(!strncmp(arg, "user=", strlen("user=")))
    {   user = strchr(arg, '=') + 1;
        return 0;
    }

    if(!strncmp(arg, "password=", strlen("password=")))
    {   password = strchr(arg, '=') + 1;
        return 0;
    }

    if(!strncmp(arg, "database=", strlen("database=")))
    {   database = strchr(arg, '=') + 1;
        return 0;
    }
 
    if(!strncmp(arg, "logfile=", strlen("logfile=")))
    {   const char * filename = strchr(arg, '=') + 1;
        FILE * file = fopen(filename, "a");
        if (file!=NULL)
        {   if (log::file!=stderr)
                fclose(log::file);
            log::file = file;
            return 0;
        }
        else
        {   perror(filename);
            return 1;
        }
    }
 
    if(!strncmp(arg, "log-level=", strlen("log-level=")))
    {
        char* options = strdup(strchr(arg, '=') + 1);
        char* options_saved = options;
        char* save_ptr;
        char* curr_option;
        
        curr_option = strtok_r(options, "|,&, ", &save_ptr);
        
        log::level = log::LOG_NONE;

        while(curr_option) {
            printf("%s\n", curr_option);
            if(!strncmp(curr_option, "ERROR", strlen("ERROR"))) {
                log::level |= log::LOG_ERROR;
            }  
            else if(!strncmp(curr_option, "WARN", strlen("WARN"))) {
                log::level |= log::LOG_WARN;
            }
            else if(!strncmp(curr_option, "INFO", strlen("INFO"))) {
                log::level |= log::LOG_INFO;
            }
            else if(!strncmp(curr_option, "DEBUG", strlen("DEBUG"))) {
                log::level |= log::LOG_DEBUG;
            }
            curr_option = strtok_r(NULL, "|,&, ", &save_ptr);
        }

        free(options_saved);

        return 0;
    }

    if(!strncmp(arg, "config=", strlen("config=")))
    {   return load_config_from_file(strchr(arg, '=') + 1);
    }
    
    return -1;
}

void Config::set_args(int argc, char ** argv)
{   free_args();
    static struct fuse_args initial_args = FUSE_ARGS_INIT(argc, argv);
    args = initial_args;
}

void Config::init_fuse_args()
{   fuse_opt_parse(&args, this, NULL, arg_processor);
}

/**
 * If arguments are allocated, frees them.
 * 
 */
 
void Config::free_args()
{   if (args.allocated)
        fuse_opt_free_args(&args);
}
